package com.juyu.miliao.common.dataSource;

import java.lang.ref.Reference;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.IntStream;

import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;

public class MultipleDataSourceParser {
	private final Object defaultDataSource;
	private final Map<Object, Object> dataSourceMap;
	private final Map<String, List<String>> subDataSourceNameMap;
	
	private MultipleDataSourceParser(Object defaultDataSource, Map<Object, Object> dataSourceMap, Map<String, List<String>> subDataSourceNameMap){
		this.defaultDataSource = defaultDataSource;
		this.dataSourceMap = dataSourceMap;
		this.subDataSourceNameMap = subDataSourceNameMap;
	}
	
	public static Builder builder(){
		return new Builder();
	}
	
	public Object getDefaultDataSource() {
		return defaultDataSource;
	}

	public Map<Object, Object> getDataSourceMap() {
		return dataSourceMap;
	}

	public List<String> getSubDataSourceNames(String dataSourceName){
		return subDataSourceNameMap.get(dataSourceName);
	}
	
	public static class Builder {
		private final Logger logger = LoggerFactory.getLogger(Builder.class);
		private MultipleDataSourceConfig mdsc;

		public MultipleDataSourceConfig getMultipleDataSourceConfig() {
			return mdsc;
		}

		public Builder setMultipleDataSourceConfig(MultipleDataSourceConfig multipleDataSourceConfig) {
			this.mdsc = multipleDataSourceConfig;
			return this;
		}
		
		public MultipleDataSourceParser build(){
			if(logger.isDebugEnabled())
				logger.debug("读取 multipleDataSourceConfig ：" + mdsc);
			AtomicReference<Object> defaultDataSourceRef = new AtomicReference<>();
			Map<Object, Object> dataSourceMap = new HashMap<>();
			Map<String, List<String>> subDataSourceNameMap = new HashMap<>();
			mdsc.getDataSource().forEach((name, list) -> {
				IntStream.range(0, list.size()).forEach(index -> {
					String key = name + "-" + index;
					Map<String, String> config = list.get(index);
					Object dateSource = createDataSource(mdsc.getPoolConfig(), key, config);
					if(logger.isDebugEnabled())
						logger.debug("创建数据库，dateSource=" + dateSource + "，key=" + key +"， poolConfig=" + mdsc.getPoolConfig() + ", dataSourceConfig=" + list.get(index));
					if(Boolean.parseBoolean(config.getOrDefault("default", "false"))) {
						if(logger.isDebugEnabled())
							logger.debug("默认数据库，dateSource=" + dateSource +"，key=" + key);
						defaultDataSourceRef.set(dateSource);
					}
					dataSourceMap.put(key, dateSource);
					subDataSourceNameMap.compute(name, (k, v) -> {
						if(v == null)
							v = new ArrayList<>();
						v.add(key);
						return v;
					});
				});
			});
			if(defaultDataSourceRef.get() == null)
				throw new NullPointerException("必须设置一个默认的数据源");
			return new MultipleDataSourceParser(defaultDataSourceRef.get(), dataSourceMap, subDataSourceNameMap);
		}

		private Object createDataSource(Map<String, String> poolConfig, String dateSourceName, Map<String, String> dataSourceConfig){
			try {
				AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
				ds.setUniqueResourceName(dateSourceName);
				ds.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
				Properties p = new Properties();
				p.setProperty("user", dataSourceConfig.get("username"));
				p.setProperty("password", dataSourceConfig.get("password"));
				p.setProperty("url", dataSourceConfig.get("url"));
				ds.setXaProperties(p);
				ds.setBorrowConnectionTimeout(Integer.parseInt(poolConfig.get("borrowConnectionTimeout")));
				ds.setTestQuery(poolConfig.get("testQuery"));
				ds.setPoolSize(Integer.parseInt(poolConfig.get("poolSize")));
				ds.setMaxIdleTime(Integer.parseInt(poolConfig.get("maxIdleTime")));
				ds.setMaxLifetime(Integer.parseInt(poolConfig.get("maxLifetime")));
				ds.setLoginTimeout(Integer.parseInt(poolConfig.get("loginTimeout")));
				return ds;
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}

	}
	
}
